/** @file    BusRingQt.cpp
 *  @author  Alessandro Polo
 *  @version $Id: BusRingQt.cpp 2115 2010-03-13 13:16:25Z alex $
 *  @brief
 * File containing methods for the wosh::gui::BusRingQt class.
 * The header for this class can be found in BusRingQt.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <framework/gui/BusRingQt.h>

 #include <core/ObjectAllocator.h>
 #include <core/BusManager.h>


using namespace std;
namespace wosh {
 namespace gui {

 WOSH_REGISTER(wosh::gui::BusRingQt, "wosh::Bus", 1.00, _static_BusRingQt )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

BusRingQt::BusRingQt( const std::string& busname ) : QObject(), Bus()
 {
	if ( !busname.empty() )
		Object::setName( busname );
	 else
		 Object::setName( Utilities::format(_BusRingQt_NAME"#%i", getBusID()) );
	Log.setContext( Object::getName() );
	Log.setLevel( LOG_INFO );

	this->timerID = 0;

	push_event( Bus::STATE_CREATED );
 }

BusRingQt::~BusRingQt()
 {
	Log(LOG_INFO, " Destroying.." );

	forward_event_to_listeners( Bus::STATE_DESTROYED);

	if ( isBusRunning() )
		stopBus();

	// message queue free messages when destroyed

	Log(LOG_VERBOSE, ":~BusRingQt() Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUS OPERATIONS

WRESULT BusRingQt::startBus( unsigned long )
 {
	if ( this->timerID != 0 ) {
		Log(LOG_WARNING, ":startBus() : Already running! Restarting.." );
		QObject::killTimer( this->timerID );
		this->timerID = QObject::startTimer(50);
		return WRET_OK;
	 }
	Log(LOG_VERBOSE, ":startBus()");
	this->timerID = QObject::startTimer(50);
	processingInitialize();

	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BusRingQt::stopBus( unsigned long )
 {
	if ( this->timerID == 0 ) {
		Log(LOG_CRITICAL, ":stopBus() : Already stopped!" );
		return WRET_ERR_ILLEGAL_USE;
	 }
	Log(LOG_VERBOSE, ":stopBus()");
	QObject::killTimer( this->timerID );
	return WRET_OK;
 }

bool BusRingQt::isBusRunning() const {
	return ( this->timerID != 0 );
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUS OPERATIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////// MESSAGING

void BusRingQt::forward_message_to_listeners( const Message* message )
 {
	if ( message == NULL ) return;

	MutexLockerRead mL(&this->nodesMux);
	vector<IBusListener*>::iterator it;
	for ( it=this->nodes.begin(); it!=this->nodes.end(); it++ ) {
		if ( *it == NULL ) continue;
		(*it)->busMessage( *message, this );
	 }
 }

void BusRingQt::forward_event_to_listeners( Bus::BUS_STATE state )
 {
	MutexLockerRead mL(&this->nodesMux);
	vector<IBusListener*>::iterator it;
	for ( it=this->nodes.begin(); it!=this->nodes.end(); it++ ) {
		if ( *it == NULL ) continue;
		(*it)->busEvent( state, this );
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BusRingQt::timerEvent( QTimerEvent* )
 {
	if ( this->msgQueue.isEmpty() )
		return;
	processingStarted();

	// get and remove it from queue
	Message* message = this->msgQueue.pop();
	if ( message == NULL ) return;

	Log(LOG_INFO, " MSG %i %s->[%s]%s | %s", message->getID(), 
			message->getSource().toString().c_str(), message->getDestinationBus().c_str(), message->getDestination().toString().c_str(),
			message->getContentClassName().c_str()	);

	// forward message to handlers (message is passed as constant)
	if ( isForMyself(message) )
		forward_message_to_listeners( message );

	// forward message to the Bus Manager, it will route message (if required)
	push_message( message );

	// finally delete the instance
	delete message;

	processingFinished();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BusRingQt::postMessage( Message* message ) const
 {
	if ( message == NULL ) return;

	Bus::assignSourceKernel( message );
	Bus::assignBus( message );

	if ( message->getID() == 0 )
		Bus::assignID( message );

	if ( !message->hasSecurityToken() )
		Log(LOG_WARNING, ":postMessage(%i) Missing SecurityToken by [%s]", message->getID(), message->getSource().toString().c_str() );

	this->msgQueue.push( message );
 }


/////////////////////////////////////////////////////////////////////////////////////////////////// MESSAGING
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////// HANDLERS

WRESULT BusRingQt::addBusListener( IBusListener* node )
 {
	if ( node == NULL ) return WRET_ERR_PARAM;

	MutexLockerWrite mL(&this->nodesMux);
	vector<IBusListener*>::iterator it;
	for (it=this->nodes.begin(); it!=this->nodes.end(); it++ ) {
		if ( *it != node ) continue;
		Log(LOG_WARNING, ":addBusListener() Listener already registered!" );
		return WRET_ERR_ILLEGAL_USE;
	 }
	this->nodes.push_back(node);
	node->busEvent( Bus::STATE_STARTED, this);
	Log(LOG_VERBOSE, ":addBusListener() Listener registered" );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BusRingQt::removeBusListener( IBusListener* node )
 {
	if ( node == NULL ) return WRET_ERR_PARAM;

	MutexLockerWrite mL(&this->nodesMux);
	vector<IBusListener*>::iterator it;
	for (it=this->nodes.begin(); it!=this->nodes.end(); it++ ) {
		if ( *it != node ) continue;
		node->busEvent( Bus::STATE_STOPPED, this );
		this->nodes.erase( it );
		Log(LOG_VERBOSE, ":removeBusListener() Listener removed" );
		return WRET_OK;
	 }
	Log(LOG_WARNING, ":removeBusListener() Listener not found!" );
	return WRET_ERR_ILLEGAL_USE;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool BusRingQt::isBusListener( const IBusListener* node ) const
 {
	if ( node == NULL ) return WRET_ERR_PARAM;

	MutexLockerRead mL(&this->nodesMux);
	vector<IBusListener*>::const_iterator it;
	for (it=this->nodes.begin(); it!=this->nodes.end(); it++ ) {
		if ( *it == node )
			return true;
	 }
	return false;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int BusRingQt::countBusListeners() const
 {
	MutexLockerRead mL(&this->nodesMux);
	return this->nodes.size();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////// HANDLERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace gui
}; // namespace wosh
